﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;

using MSOffice.Outlook;

namespace OutlookDuplicateRemover
{
    public partial class FormMain : Form
    {
        private class DictItem
        {
            public MailItem mail;
            public int Count;
            public List<MailItem> CopyList = null;
        }

        private struct ProcessArgument
        {
            public readonly Folder SrcForder;
            public readonly Folder DstForder;
            public readonly bool IsMove;
            public readonly bool IsDelete;
            public readonly bool IsCopy;
            public readonly List<string> KeyList;
            public readonly Dictionary<string, DictItem> dictMails;
            //public int missMail;

            public ProcessArgument(Folder ScrForder, Folder DstForder, bool isMove, bool isDelete, bool isCopy, List<string> keyList, Dictionary<string, DictItem> dictMails)
            {
                this.SrcForder = ScrForder;
                this.DstForder = DstForder;
                this.IsMove = isMove;
                this.IsDelete = isDelete;
                this.KeyList = keyList;
                this.IsCopy = isCopy;
                this.dictMails = dictMails;

                //this.missMail = 0;
            }
        }

        private const string HEADER = "Поиск уникальных почтовых сообщений";
        private const string REGISTER_PATH = "HKEY_CURRENT_USER\\Software\\OutlookDublicateRemove";

        private MSOffice.Outlook.Application app2 = null;
        private Folder SrcFolder = null;
        private Folder DstFolder = null;

        private Dictionary<string, DictItem> dictMails = null;
        private int missMail;
        private int copiedMail;
        private int notCopiedMail;

        public FormMain()
        {
            InitializeComponent();

            this.Text = HEADER;
            pbProgressSearch.Minimum = 0;
            pbProgressSearch.Step = 1;
            pbProgressCopy.Minimum = 0;
            pbProgressCopy.Step = 1;

            tSrc.ReadOnly = true;
            tDst.ReadOnly = true;

            cbSubject.Tag = "Subject";
            cbSender.Tag = "Sender";
            cbRecieveTime.Tag = "RecieveTime";
            cbBody.Tag = "Body";
            cbRecipient.Tag = "Recipient";
            cbBCC.Tag = "BCC";
            cbCC.Tag = "CC";
            cbTo.Tag = "To";
        }

        private void bStart_Click(object sender, EventArgs e)
        {
            try
            {
                ProcessFolder();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void ProcessFolder()
        {
            if (SrcFolder != null && DstFolder != null)
            {
                if (SrcFolder.FullFolderPath != DstFolder.FullFolderPath)
                {
                    if (IsEmptyFolder(DstFolder))
                    {
                        bool selected = false;

                        foreach (Control c in gbKey.Controls)
                        {
                            if (c is CheckBox && c.Tag != null)
                            {
                                selected = ((CheckBox)c).Checked;
                                if (selected)
                                    break;
                            }
                        }

                        if (selected)
                        {
                            bStart.Enabled = false;
                            this.Text = HEADER + " (выполняется обработка)";
                            pbProgressSearch.Maximum = SrcFolder.MailItems.Count;
                            tsslTotalCount.Text = "Сообщений: " + pbProgressSearch.Maximum;

                            dictMails = new Dictionary<string, DictItem>();
                            missMail = 0;
                            copiedMail = 0;
                            notCopiedMail = 0;

                            tsslProcessed.Text = String.Empty;
                            tsslMiss.Text = String.Empty;
                            tsslUnique.Text = String.Empty;
                            tsslCopied.Text = String.Empty;
                            tsslNotCopied.Text = String.Empty;
                            pbProgressSearch.Value = 0;
                            pbProgressCopy.Value = 0;

                            List<string> keyList = new List<string>();

                            foreach (Control c in gbKey.Controls)
                            {
                                if (c is CheckBox && c.Tag != null && ((CheckBox)c).Checked)
                                    keyList.Add((string)c.Tag);
                            }

                            bwSearchProcessor.RunWorkerAsync(new ProcessArgument(SrcFolder, DstFolder, cbMove.Checked, cbDelete.Checked, cbCopy.Checked, keyList, dictMails));
                        }
                        else
                            MessageBox.Show("Выберите критерий уникальности", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    else
                        MessageBox.Show("Папка-назначения должна быть пустой", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                else
                    MessageBox.Show("Папка-источник и папка-назначения не должны совпадать", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
                MessageBox.Show("Выберите папки", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        private static bool IsEmptyFolder(Folder DstFolder)
        {
            return DstFolder.Folders.Count == 0 && DstFolder.MailItems.Count == 0;
        }

        private static string MakeKey(MailItem mail, ProcessArgument param)
        {
            StringBuilder res = new StringBuilder();

            foreach (string prop in param.KeyList)
            {
                string str = null;

                switch(prop)
                {
                    case "Subject" : str = mail.Subject; break;
                    case "Sender" : str = mail.SenderName; break;
                    case "RecieveTime" : str = mail.ReceivedTime.ToString(); break;
                    case "Body" : str = mail.Body; break;
                    case "Recipient" : str = mail.ReceivedByName; break;
                    case "BCC" : str = mail.BCC; break;
                    case "CC" : str = mail.CC; break;
                    case "To" : str = mail.To; break;
                }

                res.Append(res.Length == 0 ? str : "; " + str);
            }

            return res.ToString();
        }

        private void wbProcessor_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker bw = (BackgroundWorker)sender;
            Folder SrcFolder = ((ProcessArgument)e.Argument).SrcForder;
            Dictionary<string, DictItem> dictMails = ((ProcessArgument)e.Argument).dictMails;

            for (int i = 1; i <= SrcFolder.MailItems.Count; i++)
            {
                try
                {
                    MailItem mail = SrcFolder.MailItems[i];

                    string key = MakeKey(mail, (ProcessArgument)e.Argument);

                    if (!dictMails.ContainsKey(key))
                    {
                        DictItem item = new DictItem();
                        item.mail = mail;
                        item.Count = 1;
                        dictMails.Add(key, item);
                    }
                    else
                    {
                        dictMails[key].Count++;

                        if (dictMails[key].CopyList == null)
                            dictMails[key].CopyList = new List<MailItem>();

                        dictMails[key].CopyList.Add(mail);
                    }
                }
                catch (Exception)
                {
                    missMail++;
                }

                bw.ReportProgress(i);
            }

            e.Result = e.Argument;
        }

        private void wbProcessor_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            pbProgressSearch.Value = e.ProgressPercentage;

            tsslProcessed.Text = "Найдено: " + pbProgressSearch.Value;
            tsslMiss.Text = "Пропущено: " + missMail;
            tsslUnique.Text = "Уникальных: " + dictMails.Count;
        }

        private void wbProcessor_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            pbProgressCopy.Maximum = dictMails.Count;
            bwCopyProcessor.RunWorkerAsync(e.Result);
        }

        private void bwCopyProcessor_DoWork(object sender, DoWorkEventArgs e)
        {
            bool isMove = ((ProcessArgument)e.Argument).IsMove;
            bool isDelete = ((ProcessArgument)e.Argument).IsDelete;
            bool isCopy = ((ProcessArgument)e.Argument).IsCopy;
            Dictionary<string, DictItem> dictMails = ((ProcessArgument)e.Argument).dictMails;
            
            BackgroundWorker bw = (BackgroundWorker)sender;
            //Folder SrcFolder = ((ProcessArgument)e.Argument).SrcForder;
            Folder DstFolder = ((ProcessArgument)e.Argument).DstForder;

            int i = 0;
            foreach (DictItem item in dictMails.Values)
            {
                try
                {
                    MailItem mailCopy = item.mail;

                    if (isCopy)
                    {
                        //DstFolder.MailItems.Add(mailCopy);
                    }
                    else if (isMove)
                        mailCopy.Move(DstFolder);

                    if (isDelete && item.CopyList != null)
                    {
                        foreach (MailItem c in item.CopyList)
                            c.Delete();
                    }

                    copiedMail++;
                }
                catch (Exception)
                {
                    notCopiedMail++;
                }

                bw.ReportProgress(i++);
            }
        }

        private void bwCopyProcessor_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            pbProgressCopy.Value = e.ProgressPercentage;
            tsslCopied.Text = "Обработано: " + copiedMail;
            tsslNotCopied.Text = "Не обработано: " + notCopiedMail;
        }

        private void bwCopyProcessor_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bStart.Enabled = true;
            this.Text = HEADER + " (обработка завершена)";
            pbProgressCopy.Value = dictMails.Count;
        }

        private void bSrc_Click(object sender, EventArgs e)
        {
            SrcFolder = GetFolder(tSrc.Text);

            if (SrcFolder != null)
                tSrc.Text = SrcFolder.FullFolderPath;
        }

        private void bDst_Click(object sender, EventArgs e)
        {
            DstFolder = GetFolder(tDst.Text);

            if (DstFolder != null)
                tDst.Text = DstFolder.FullFolderPath;
        }

        private Folder GetFolder(string path)
        {
            Folder res = null;

            try
            {
                OutlookFolderSelectForm frm = new OutlookFolderSelectForm(app2, path);

                if (frm.ShowDialog() == DialogResult.OK)
                    res = frm.Folder;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            return res;
        }

        private void FormMain_Load(object sender, EventArgs e)
        {
            try
            {
                app2 = new MSOffice.Outlook.Application();
            }
            catch
            {
                MessageBox.Show("Не удалось загрузить OUTLOOK");

                bStart.Enabled = false;
                bSrc.Enabled = false;
                bDst.Enabled = false;
            }

            if (app2 != null)
            {
                OutlookFolderSelectForm frm = new OutlookFolderSelectForm(app2);

                try
                {
                    tSrc.Text = (string)Registry.GetValue(REGISTER_PATH, "SrcFolder", String.Empty);
                    if (!String.IsNullOrEmpty(tSrc.Text))
                        SrcFolder = frm.GetFolder(tSrc.Text);
                }
                catch
                {
                    tSrc.Text = String.Empty;
                }

                try
                {
                    tDst.Text = (string)Registry.GetValue(REGISTER_PATH, "DstFolder", String.Empty);
                    if (!String.IsNullOrEmpty(tDst.Text))
                        DstFolder = frm.GetFolder(tDst.Text);
                }
                catch
                {
                    tDst.Text = String.Empty;
                }
            }

            cbSubject.Select();
        }

        private void cbMove_CheckedChanged(object sender, EventArgs e)
        {
            if (cbMove.Checked)
                cbCopy.Checked = false;
        }

        private void cbCopy_CheckedChanged(object sender, EventArgs e)
        {
            if (cbCopy.Checked)
                cbMove.Checked = false;
        }

        private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (SrcFolder != null)
                Registry.SetValue(REGISTER_PATH, "SrcFolder", SrcFolder.FullFolderPath);

            if (DstFolder != null)
                Registry.SetValue(REGISTER_PATH, "DstFolder", DstFolder.FullFolderPath);
        }

        private void bHelp_Click(object sender, EventArgs e)
        {
            const string strHelp = 
                "Программа предназначена для освобождения папки OUTLOOK от дублирующих сообщений.\r\n" +
                "Перед началом работы лучше скопировать все сообшения в отдельную папку OUTLOOK.\r\n" +
                "Если не указывать 'Копировать уникальные' и/или 'Перемещать уникальные' и/или 'Удалять копии', то\r\n" +
                "программа просто просканирует папку OUTLOOK.\r\n\r\n" +
                "В процессе работы может появиться окно с запросом ввода пароля и времени разрешения доступа к OUTLOOK - \r\n" +
                "это нормальное поведение."
                ;

            MessageBox.Show(strHelp);
        }
    }
}
